home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Libris Britannia 4
/
science library(b).zip
/
science library(b)
/
UTILITIE
/
CPU_MEMO
/
0978.ZIP
/
FILES3X.ASM
< prev
next >
Wrap
Assembly Source File
|
1987-07-03
|
18KB
|
387 lines
;****************************************************************************
; file: files3x.asm by: Steven M. Gibson, Irvine, CA created: 06/06/87
;****************************************************************************
;
; * * * PUBLIC DOMAIN COPYRIGHT RELEASE NOTICE * * *
;
; THIS PROGRAM, IN BOTH SOURCE CODE AND OBJECT FORM, HAS BEEN EXPLICITLY
; PLACED INTO THE PUBLIC DOMAIN BY ITS SOLE AUTHOR AND OWNER, STEVEN GIBSON,
; OF IRVINE, CA. IT MAY THEREFORE BE FREELY REPRODUCED, EXCHANGED, UPLOADED
; AND DOWNLOADED. HOWEVER THE AUTHOR REQUESTS THAT THIS NOTICE OF RELEASE
; AND ORIGIN OF AUTHORSHIP BE LEFT INTACT AND THAT THIS PROGRAM OR ITS DIRECT
; DERRIVATIVES, IF ANY, *NOT* BE SOLD FOR PROFIT. ALSO, PLEASE KEEP THE
; ENTIRE SET OF FILES TOGETHER AS ONE PACKAGE. ----> Thanks
;
;****************************************************************************
;
; About The Program: FILES.COM (source code file: files3x.asm)
;
; "FILES.COM" is a small resident (TSR) program which overcomes a major
; glitch in the way IBM has implemented DOS 3.30. Specifically, it handles
; the requirements of explicitly ASKING DOS for additional handles and
; keeping a block of RAM available for DOS when this request is made.
; Any program can now have up to 256 files open when under 3.0-3.2 (counting
; the standard pre-opened default files) if the command "FILES" is issued
; first. This program also has the interesting ability to spontaneously
; remove itself ("Un-TSR") from memory after doing its job.
;
; If you wish to suppress the message FILES delivers when it is run, simply
; put anything after the command, (like: "files shhh") and it won't say a
; word as it goes resident. Otherwise it makes a short (non-commercial)
; statement of its intent as it terminates.
;
; NOTE: This program *ONLY* makes sense when under versions 3.0 thru 3.2
; of DOS, and will refuse to run under any earlier DOS versions.
; Use the other DOS 3.3 version of FILES.COM (source: files3x.asm)
;****************************************************************************
;
; About These SOURCE CODE FILES:
;
; This source code file, and the companion OPENER.ASM source code file, were
; written to be instructional, clear, and a bit tutorial in nature. As such
; they have been commented more heavily than normal self-communication
; would normally dictate. I hope you will find them interesting, useful,
; and not overly verbose. They are also examples of a general coding style
; I've found to endure quite well. Adopt it if you like it.
;
; NOTE: These files were created using the incredible file editor: BRIEF
;
;****************************************************************************
;
; To make a COM file from this ASM source code:
;
; masm files3x, files3x; <--- assemble the .asm to .obj
; link files3x; <--- link the .obj to .exe
; exe2bin files3x files3x.com <--- convert .exe to .com
; del files3x.obj <--- delete the intermediate debris
; del files3x.exe
;
;****************************************************************************
;----------------------------------------------------------------------------
; E Q U A T E S
;----------------------------------------------------------------------------
CR equ 0Dh
LF equ 0Ah
COM_TERMINATE equ 20h ; .COM program termination
DOS_FUNC equ 21h ; Interrupt to call DOS
DOS_PRINTSTRING equ 09h ; Dos Sub-Function Defs
DOS_SET_VECTOR equ 25h
DOS_VERSION_NUMBER equ 30h ; " "
DOS_STAY_RESIDENT equ 31h
DOS_GET_VECTOR equ 35h
DOS_CREATE equ 3Ch ; " "
DOS_OPEN equ 3DH
DOS_ALLOC equ 48H
DOS_DEALLOC equ 49H
DOS_SETBLOCK equ 4AH
DOS_GET_PSP equ 62h
SET_HANDLE_COUNT equ 67h
NEW_MAX_HANDLES equ 256 ; assuming 256 handle params
MINIMUM_VERSION equ 3 * 256 + 00 ; later than ver: "3"."00"
MAXIMUM_VERSION equ 3 * 256 + 30 ; earlier than ver: "3"."30"
HANDLE_CLOSED_FLAG equ 0FFh
;----------------------------------------------------------------------------
; M A C R O S
;----------------------------------------------------------------------------
zero MACRO p1 ; this little macro is just too
xor p1,p1 ; handy to be without. Since it
ENDM ; "cleanly" zeros any register.
;----------------------------------------------------------------------------
; C O D E S E G M E N T
;----------------------------------------------------------------------------
CODESEG SEGMENT BYTE PUBLIC
ASSUME CS:CODESEG, DS:CODESEG
ORG 02Ch ; pointer to this program's environ
Environment LABEL WORD
ORG 032h ; maximum process file handles
MaxHandleCount LABEL WORD
ORG 034h ; offset of the handle pointer table
HandleTableOff LABEL WORD
ORG 036h ; maximum process file handles
HandleTableSeg LABEL WORD
ORG 080h
ParameterCount LABEL BYTE ; count of the command-line params
ORG 100h ; .com programs execute from 0100h
ComStart: jmp TransientCode ; jump past the resident portion
;****************************************************************************
; RESIDENT CODE PORTION BEGINS HERE
;****************************************************************************
OldDosInt dd ? ; original int 21 vector pointer
HoleSegment dw ? ; segment location of the "hole"
;----------------------------------------------------------------------------
; NOTE TO THE READER: The following "resident portion" of the TSR will make
; *MUCH* more sense if you have FIRST read the "transient portion" below. I
; suggest that you jump ahead and read that first, then come back to here....
;----------------------------------------------------------------------------
Int21Intercept:
;----------------------------------------------------------------------------
; The transient portion of this program pointed DOS' calling Interrupt 21
; here before terminating itself. Therefore we receive control every time
; anyone does an Int21h call to DOS. (Until we've fulfilled our mission)
;----------------------------------------------------------------------------
cmp ah, DOS_OPEN ; was the call an open handle?
je MakeRoom ; yep, see if we're alive
cmp ah, DOS_CREATE ; was it a create handle?
je MakeRoom ; yep ....
AbortIntercept: jmp Int21Continue ; (too far for a cond. jmp)
MakeRoom: cmp cs:HoleSegment, 0 ; did we already do our thing?
je AbortIntercept ; yep, so don't do it again!
push ax ; save the caller's calling
push bx ; parameters on his own stack
push cx
push si
push di
push ds
push es
;----------------------------------------------------------------------------
; Free up the memory block, located at "HoleSegment", which was previously
; allocated by (and therefore owned by) our transient portion
;----------------------------------------------------------------------------
mov es, cs:HoleSegment ; get the segment number
mov cs:HoleSegment, 0 ; zero it so we don't again
mov ah, DOS_DEALLOC
int DOS_FUNC ; and release that ram block
;----------------------------------------------------------------------------
; And now we reallocate it (or maybe some other free chunk lying around)
; to the CURRENTLY running process's identity. This way the current
; process "owns" the block so it too will be released when he terminates.
;----------------------------------------------------------------------------
mov bx, (NEW_MAX_HANDLES+15)/16 ; use paragraphs
mov ah, DOS_ALLOC
int DOS_FUNC
mov es, ax ; and hold onto the block's segment
;----------------------------------------------------------------------------
; Now we copy the process's existing handle table into our new table
;----------------------------------------------------------------------------
mov ah, DOS_GET_PSP ; so get his PSP segment
int DOS_FUNC
mov ds, bx
push ds ; keep it safe for later
mov cx, MaxHandleCount ; get the number of handles
mov bx, cx ; and keep it for a minute
mov si, HandleTableOff ; get the offset
mov ds, HandleTableSeg ; and segment of the table
zero di
rep movsb ; and copy the table
;----------------------------------------------------------------------------
; Now we must flag the remaining handles as being still closed
;----------------------------------------------------------------------------
mov cx, NEW_MAX_HANDLES
sub cx, bx ; subtract those already done
jz SetupTable ; nothing left to do
mov al, HANDLE_CLOSED_FLAG
rep stosb
;----------------------------------------------------------------------------
; Now "activate" our new table by pointing his PSP pointers to our table
;----------------------------------------------------------------------------
SetupTable: pop ds ; recover caller's PSP
mov MaxHandleCount, NEW_MAX_HANDLES
mov HandleTableOff, 0 ; offset of the new table
mov HandleTableSeg, es ; and the new segment
;----------------------------------------------------------------------------
; Now we verify that interrupt 21 is still pointing at us, and if so
; we're able to re-point it to where it was originally pointing,
; un-vectoring ourselves) then release even ourselves from ram as well!
;----------------------------------------------------------------------------
mov ah, DOS_GET_VECTOR
mov al, DOS_FUNC ; get int 21's address
int DOS_FUNC
cmp bx, OFFSET Int21Intercept
jne RestoreStack ; it moved, so we must remain
mov ax, es ;
mov bx, cs ; can't compare segment regs,
cmp ax, bx ; <sigh> so we do it this way
jne RestoreStack
;----------------------------------------------------------------------------
; DOS' calling Interrupt 21 is still pointing at us, (as it should always be
; unless some later resident program or the currently executing program has
; changed it), so we're free to point it back to where it was pointing.
;----------------------------------------------------------------------------
push dx ; we need dx too, so stack it
mov dx, WORD PTR cs:OldDosInt
mov ds, WORD PTR cs:OldDosInt+2
mov ah, DOS_SET_VECTOR
mov al, DOS_FUNC
int DOS_FUNC
pop dx ; and restore it
;----------------------------------------------------------------------------
; Now, since we're no longer in the Interrupt 21 calling chain, we're free
; to leave ram too, so now we can release our own code segment as well!
; Since the transient portion already freed our environment allocation, and
; we just unhooked ourselves from Int21, this final release of our code seg
; will create a contiguous free region underneath the currently running
; application program which DOS will collect together and recover as soon
; as that programs terminates. This means that we will have completely
; and spontaneously removed ourselves from RAM.
;----------------------------------------------------------------------------
mov ax, cs ; get our own code segment
mov es, ax
mov ah, DOS_DEALLOC
int DOS_FUNC ; and release our ram block
;----------------------------------------------------------------------------
; Now we restore the caller's original parameters (remember, we got control
; because he was doing a DOS call himself) and pass control on to DOS so
; that the caller's original job can be performed as if we were never here.
;----------------------------------------------------------------------------
RestoreStack: pop es ; now restore our working
pop ds
pop di
pop si
pop cx
pop bx ; registers
pop ax
Int21Continue: jmp cs:[OldDosInt]
;****************************************************************************
; END OF THE RESIDENT CODE
;****************************************************************************
;----------------------------------------------------------------------------
;****************************************************************************
; BEGINNING OF THE TRANSIENT (NON-RESIDENT) CODE
;****************************************************************************
;----------------------------------------------------------------------------
; This program only operates under versions of DOS from 3.0 through 3.2,
; so we refuse to operate at all if we're using any other DOS's.
;----------------------------------------------------------------------------
TransientCode: mov ah, DOS_VERSION_NUMBER ; first we need to make sure
int DOS_FUNC ; this guy has DOS 3.3!
xchg ah, al ; make the number linear
cmp ax, MINIMUM_VERSION
jb BadDosVersion ; at least it's not too early
cmp ax, MAXIMUM_VERSION
jb MakeUsResident ; and not too late...
BadDosVersion: mov dx, OFFSET WrongDosMsg ; nope, he's using a version
mov ah, DOS_PRINTSTRING ; of DOS prior to 3.3
int DOS_FUNC
int COM_TERMINATE ; we're not "supposed" to quit
; this way anymore, but it's
; so easy!
MakeUsResident:
;----------------------------------------------------------------------------
; When DOS loads a program it is allocated ALL of the remaining contiguous
; memory from its load-point to the end of ram, so we start by modifying
; this allocation so that it just encompasses our own program's code...
;----------------------------------------------------------------------------
mov bx, OFFSET EndOfTransient + 15 ; get our ending offset
mov cl, 4 ; and compute the number of
shr bx, cl ; segments (the 15 rounds up)
mov ah, DOS_SETBLOCK ; now shrink our memory block
int DOS_FUNC
;----------------------------------------------------------------------------
; Every program also receives a set of strings in its Environment Block.
; Since we don't need this, it's polite to release it too. (Actually it's
; necessary if we, being a TSR, are ever to completely leave RAM.)
; (Dos manages memory allocation on a segment-by-segment, paragraph-by-
; paragraph basis. So we always refer to allocated blocks of memory by
; referencing that block's segment number. In the case of our Environment
; segment, our own PSP (program segment prefix) has a pointer to its seg.)
;----------------------------------------------------------------------------
mov es, Environment ; get our environment's seg
mov ah, DOS_DEALLOC
int DOS_FUNC ; and release it too!
;----------------------------------------------------------------------------
; Now we grab up a block of memory large enough to allow DOS to open 256
; files.
;----------------------------------------------------------------------------
mov bx, (NEW_MAX_HANDLES+15)/16 ; use paragraphs
mov ah, DOS_ALLOC
int DOS_FUNC
mov HoleSegment, ax ; and save the block's segment
;----------------------------------------------------------------------------
; Now, since our whole methodology involves having the resident portion
; FREE UP this newly allocated ram block, then ask DOS for a lot of handles
; WHEN the next-to-run program asks DOS to open or create a file for it, we
; need to intercept all subsequent DOS calls until we're done with our job.
; So we re-route Interrupt 21 through our resident half.
;----------------------------------------------------------------------------
mov ah, DOS_GET_VECTOR ; get the current
mov al, DOS_FUNC ; interrupt 21 value
int DOS_FUNC
mov WORD PTR OldDosInt , bx ; saving it in the
mov WORD PTR OldDosInt+2, es ; resident portion
mov dx, OFFSET Int21Intercept ; that done, we set
mov ah, DOS_SET_VECTOR ; int 21 to point
mov al, DOS_FUNC ; to our resident
int DOS_FUNC ; module
;----------------------------------------------------------------------------
; Nearly done, and just to be quite clear, we say something appropriate to
; our operator before we terminate ... but *ONLY* if he didn't specifically
; ask as to remain silent. The byte at location 80h in our PSP contains
; the count of parameters on our invoking command line. If this is not
; zero, we're suppose to suppress all departing exclamation....
;----------------------------------------------------------------------------
cmp ParameterCount, 0 ; is it zero?
jne NowWeTSR ; nope, so be quiet!
mov dx, OFFSET FilesMessage ; yep, so we get to
mov ah, DOS_PRINTSTRING ; say goodbye now
int DOS_FUNC
;----------------------------------------------------------------------------
; Finally we tell DOS that we're all through, asking it nicely to terminate
; us but leave the top half of us resident. That top half will be awakened
; every time DOS is called until it's done its job. "TransientCode" is the
; beginning of our second half, and the "+15" does the right thing with
; rounding as we convert into our paragraph size.
;----------------------------------------------------------------------------
NowWeTSR: mov ah, DOS_STAY_RESIDENT ; compute the number
mov dx, OFFSET TransientCode + 15 ; of paragraphs to
mov cl, 4 ; keep resident
shr dx, cl
int DOS_FUNC ; this "call" never returns from DOS
;----------------------------------------------------------------------------
; T E X T M E S S A G E S
;----------------------------------------------------------------------------
WrongDosMsg db CR,LF, "This program only applies to DOS versions "
db "3.00 thru 3.20 ... Sorry!",CR,LF,"$"
FilesMessage db CR,LF
db "The next program to run may open up to "
db 22h,"FILES=",22h," files." ; asc(")=22h
db CR,LF,"$"
;----------------------------------------------------------------------------
EndOfTransient = THIS BYTE ; the address of this label is used
; to compute our load-module size
CODESEG ENDS
END ComStart ; and that's all there is to it!